;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Image class and methods
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defclass image () 
  ((num-rows :initarg :m :reader image-num-rows)
   (num-cols :initarg :n :reader image-num-cols)
   (depth :initarg :depth :reader image-depth)
   (data :initarg :data  :accessor image-data))
  (:documentation "Classe delle immagini"))

;;Method: get-pixel
(defgeneric get-pixel (img i j)
  (:documentation "Returns the value of pixel (i,j) if the given image"))

(defmethod get-pixel ((img image) (i integer) (j integer))
  (aref (image-data img) (- i 1) (- j 1)))

;;Method: set-pixel
(defgeneric set-pixel (img i j value)
  (:documentation "Set pixel (i,j) of img to the given value"))

(defmethod set-pixel ((img image) (i integer) (j integer) (value number))
  (setf (aref (image-data img) i j) value))

;;Function: create-image-from-file
(defun create-image-from-file (filename)
  (with-open-file (in filename)
    (with-standard-io-syntax
      (progn
	(read-line in) ; P2
	(read-line in) ; comment #
	(let* ((n (read in))
	       (m (read in))
	       (depth (read in))
	       (data (make-array (list m n) 
				 :element-type 'number
				 :initial-contents (loop for i from 1 to m collecting
							(loop for j from 1 to n collecting (read in))))))
	  (make-instance 'image :m m :n n :depth depth :data data))))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Ant class and methods
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defclass ant ()
  ((name :initarg :name :reader ant-name)
   (path-num-rows :initarg :path-num-rows :reader ant-path-num-rows)
   (path-num-cols :initarg :path-num-cols :reader ant-path-num-cols)
   (path :initarg :path :accessor ant-path))
  (:documentation "Formichina!!!!"))

(defun create-ant (name rows cols)
  (make-instance 'ant 
		 :name name 
		 :path-num-rows rows 
		 :path-num-cols cols 
		 :path (make-array (list rows cols) 
				   :element-type 'list 
				   :initial-element (list 0 0))))


(defgeneric get-shift (i j bug)
  (:documentation "Retrive the shift of pixel i j choosen the ant."))

(defmethod get-shift ((i integer) (j integer) (bug ant))
  (aref (ant-path bug) (- i 1) (- j 1)))

(defgeneric set-shift (i j bug shift)
  (:documentation "Set the shift choosen by anty for pixel i j."))

(defmethod set-shift ((i integer) (j integer) (bug ant) shift)
  (setf (aref (ant-path bug) (- i 1) (- j 1)) shift))



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Mixed method for ants and images
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defgeneric build-path (bug img)
  (:documentation "The ant BUG build a path from image img"))

(defmethod build-path ((bug ant) (img image))
  (loop for i from (image-num-rows img) do
       (loop for j from (image-num-cols img) do
	    (set-shift i j bug (choose-hij i j img bug)))))


(defgeneric choose-hij (i j img bug)
  (:documentation "Scelta del pixel i j nell'immagine img"))

(defmethod choose-hij ((i integer) (j integer) (img image) (bug ant))
  (let ((PH (aref *PH* (- i 1) (- j 1)))) 
    (build-choices PH (get-new-shifts 3))))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Stuff for testing
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defvar *test* (create-image-from-file "test.pgm"))
(defvar *test-shifted* (create-image-from-file "test-shifted.pgm"))
(defvar *spostamento-massimo* 100)

(defun shift-difference (shift1 shift2)
  (list (- (first shift1) (first shift2)) (- (second shift1) (second shift2))))

(defun shift-sum (shift1 shift2)
  (list (+ (first shift1) (first shift2)) (+ (second shift1) (second shift2))))


(defun g (x y)
  (log (+ (* x x) (* y y) 1)))
  

(defvar *matrice-dei-pesi* (make-hash-table :key-type 'list :value-type 'number :test #'equalp))

(defun add-weight-entry(i j)
  (setf (gethash (list i j) *matrice-dei-pesi*) (g i j)))

(defun get-weight (i j)
  (let* ((h (abs i))
	 (k (abs j))
	 (hh (max h k))
	 (kk (min h k)))
    (or (gethash (list hh kk) *matrice-dei-pesi*)
	(add-weight-entry hh kk))))


(loop for i from 0 to *spostamento-massimo* do
	      (loop for j from 0 to *spostamento-massimo* do (get-weight i j)))


(defvar *PH* 
  (make-array 
   (list (image-num-rows *test*) (image-num-cols *test*)) 
   :initial-element (make-hash-table 
		     :key-type 'list 
		     :value-type 'number
		     :test #'equalp)))
;; EURISTICA
;; selezione degli spostamenti che non aumentano il funzionale
;; di una certa soglia
(remove-if-not #'(lambda (key) (> 3 (gethash key *matrice-dei-pesi*)))
	       (loop for key being the hash-keys of *matrice-dei-pesi* collecting key))





(defun vertical-shift (shift)
  (car (cdr shift)))

(defun horizontal-shift (shift)
  (car shift))


		   
  

(defun build-choices (visited  unvisited)
  (append
   (loop for key being the hash-keys of visited 
      using (hash-value value)
	collecting (list key value))
   (loop for key in unvisited when (not (gethash key visited)) collecting (list key 1))))

(defun get-new-shifts (max)
  (remove-if-not #'(lambda (key) (> max (gethash key *matrice-dei-pesi*)))
		 (loop for key being the hash-keys of *matrice-dei-pesi* collecting key)))

